library(tidyverse) 

Scenarios

True control concentration -> 4pg/ml
inter-cv -> 15 %
intra-cv -> 5 %

The ‘between’ plate SD will be Inter-CVxTrueMean/100 - QUANTITY WE WANT TO ESIMTATE/SET OUR LIMITS ON

true_mean <- 4  
inter_cv <- 15  
intra_cv <- 5  
between_plate_sd <- inter_cv*true_mean/100 
within_plate_sd <- intra_cv*true_mean/100



reps <- 10
plate_rep <- data.frame(rep_type = seq(1:reps), 
                        quad_i = rep()) 
Error in data.frame(rep_type = seq(1:reps), quad_i = rep()) : 
  arguments imply differing number of rows: 10, 0
plates <- 3
plate <- data.frame(plate = seq(1:plates), 
                    plate_i = rnorm(plates, true_mean, between_plate_sd))

plate
simulating_xmap_data <- function(plates_num = 3,
                                 reps_num = 8,
                                 true_mean = 4,
                                 inter_cv = 15,
                                 intra_cv = 5) {
  
  between_plate_sd <- inter_cv*true_mean / 100
  within_plate_sd <- intra_cv*true_mean / 100
  
  plates <- plates_num
  
  plate <- data.frame(
    plate = seq(1:plates),
    plate_i = rnorm(plates, true_mean, between_plate_sd)
  )
  
  reps <- reps_num
  plate_rep <- data.frame(rep_type = seq(1:reps))
  
  sim_plates <- tidyr::crossing(plate, plate_rep) %>%
    mutate(response = plate_i + rnorm(nrow(.), 0, within_plate_sd))
  
  
  sd_all <- sim_plates %>%
    summarise(sd = sd(response)) %>%
    pull()
  
  sd_plate <- sim_plates %>%
    group_by(plate) %>%
    summarise(mean = mean(response)) %>%
    summarise(sd = sd(mean)) %>%
    select(sd) %>%
    pull()
  
  vec <- data.frame(sd_all_reps = sd_all, sd_all_plates = sd_plate)
  return(vec)
  
}

Fix plates, vary reps

sim_over_plates <- imap_dfr(1:1000, ~ map_dfr(3:20, ~ 
                                                simulating_xmap_data(plates_num = .x), 
                                            .id = "plates"), 
         .id = "sim") %>% 
  mutate(plates = as.double(plates)) %>% 
  mutate(plates = plates + 3)

Fix reps, vary plates

Vary both plates and reps

params <- crossing(reps = seq(from = 4, to = 12, 
                              by = 2), 
                   plates = seq(from  = 3, to = 20, by = 2))

sim_param <- imap_dfr(1:1000, ~ params %>% 
  rowwise() %>% 
  summarise(results = simulating_xmap_data(plates_num = plates, 
                                           reps_num = reps)) %>% 
  unnest(cols = c(results)) %>% 
  bind_cols(params), 
  .id = "sim")
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgCmZyZWVub21lX2NvbG9ycyA8LSBjKCcjOTQ4REZGJywgJyMxREUzRkUnLCAnI0JCRDUzMicsICcjRkY5RDQyJywgICcjRkMyRTdCJywgCiAgICAgICAgICAgICAgICAgICAnI0ZFQ0REMScpCmBgYAoKCiMjIyBTY2VuYXJpb3MgICAKCiAgIAoKVHJ1ZSBjb250cm9sIGNvbmNlbnRyYXRpb24gLT4gIDRwZy9tbCAgIAppbnRlci1jdiAtPiAxNSAlICAKaW50cmEtY3YgLT4gNSAlICAgCgpUaGUgJ2JldHdlZW4nIHBsYXRlIFNEIHdpbGwgYmUgSW50ZXItQ1Z4VHJ1ZU1lYW4vMTAwICAtIFFVQU5USVRZIFdFIFdBTlQgVE8gRVNJTVRBVEUvU0VUIE9VUiBMSU1JVFMgT04gICAKCmBgYHtyfQp0cnVlX21lYW4gPC0gNCAgCmludGVyX2N2IDwtIDE1ICAKaW50cmFfY3YgPC0gNSAgCmJldHdlZW5fcGxhdGVfc2QgPC0gaW50ZXJfY3YqdHJ1ZV9tZWFuLzEwMCAKd2l0aGluX3BsYXRlX3NkIDwtIGludHJhX2N2KnRydWVfbWVhbi8xMDAgCmBgYApgYGB7cn0KcGxhdGVzIDwtIDMKcGxhdGUgPC0gZGF0YS5mcmFtZShwbGF0ZSA9IHNlcSgxOnBsYXRlcyksIAogICAgICAgICAgICAgICAgICAgIHBsYXRlX2kgPSBybm9ybShwbGF0ZXMsIHRydWVfbWVhbiwgYmV0d2Vlbl9wbGF0ZV9zZCkpCgpwbGF0ZQpgYGAKYGBge3J9CnJlcHMgPC0gNApwbGF0ZV9yZXAgPC0gZGF0YS5mcmFtZShyZXBfdHlwZSA9IHNlcSgxOnJlcHMpKQoKc2ltX3BsYXRlcyA8LSB0aWR5cjo6Y3Jvc3NpbmcocGxhdGUsIHBsYXRlX3JlcCkgJT4lIAogIG11dGF0ZShyZXNwb25zZSA9IHBsYXRlX2kgKyBybm9ybShucm93KC4pLCAwLCB3aXRoaW5fcGxhdGVfc2QpKSAKc2ltX3BsYXRlcwpgYGAKCgoKYGBge3J9CnNpbXVsYXRpbmdfeG1hcF9kYXRhIDwtIGZ1bmN0aW9uKHBsYXRlc19udW0gPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBzX251bSA9IDgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWVfbWVhbiA9IDQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyX2N2ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludHJhX2N2ID0gNSkgewogIAogIGJldHdlZW5fcGxhdGVfc2QgPC0gaW50ZXJfY3YqdHJ1ZV9tZWFuIC8gMTAwCiAgd2l0aGluX3BsYXRlX3NkIDwtIGludHJhX2N2KnRydWVfbWVhbiAvIDEwMAogIAogIHBsYXRlcyA8LSBwbGF0ZXNfbnVtCiAgCiAgcGxhdGUgPC0gZGF0YS5mcmFtZSgKICAgIHBsYXRlID0gc2VxKDE6cGxhdGVzKSwKICAgIHBsYXRlX2kgPSBybm9ybShwbGF0ZXMsIHRydWVfbWVhbiwgYmV0d2Vlbl9wbGF0ZV9zZCkKICApCiAgCiAgcmVwcyA8LSByZXBzX251bQogIHBsYXRlX3JlcCA8LSBkYXRhLmZyYW1lKHJlcF90eXBlID0gc2VxKDE6cmVwcykpCiAgCiAgc2ltX3BsYXRlcyA8LSB0aWR5cjo6Y3Jvc3NpbmcocGxhdGUsIHBsYXRlX3JlcCkgJT4lCiAgICBtdXRhdGUocmVzcG9uc2UgPSBwbGF0ZV9pICsgcm5vcm0obnJvdyguKSwgMCwgd2l0aGluX3BsYXRlX3NkKSkKICAKICAKICBzZF9hbGwgPC0gc2ltX3BsYXRlcyAlPiUKICAgIHN1bW1hcmlzZShzZCA9IHNkKHJlc3BvbnNlKSkgJT4lCiAgICBwdWxsKCkKICAKICBzZF9wbGF0ZSA8LSBzaW1fcGxhdGVzICU+JQogICAgZ3JvdXBfYnkocGxhdGUpICU+JQogICAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKHJlc3BvbnNlKSkgJT4lCiAgICBzdW1tYXJpc2Uoc2QgPSBzZChtZWFuKSkgJT4lCiAgICBzZWxlY3Qoc2QpICU+JQogICAgcHVsbCgpCiAgCiAgdmVjIDwtIGRhdGEuZnJhbWUoc2RfYWxsX3JlcHMgPSBzZF9hbGwsIHNkX2FsbF9wbGF0ZXMgPSBzZF9wbGF0ZSkKICByZXR1cm4odmVjKQogIAp9CmBgYAoKKipGaXggcGxhdGVzLCB2YXJ5IHJlcHMqKiAgIAoKYGBge3J9CnNpbV9vdmVyX3JlcHMgPC0gaW1hcF9kZnIoMToxMDAwLCB+IG1hcF9kZnIoNDoxNiwgfiBzaW11bGF0aW5nX3htYXBfZGF0YShyZXBzX251bSA9IC54KSwgLmlkID0gInJlcHMiKSwgCiAgICAgICAgIC5pZCA9ICJzaW0iKSAlPiUgCiAgbXV0YXRlKHJlcHMgPSBhcy5kb3VibGUocmVwcykpICU+JSAKICBtdXRhdGUocmVwcyA9IHJlcHMgKyAzKQoKCnNpbV9vdmVyX3JlcHMgJT4lIAogIGdncGxvdCguLGFlcyhyZXBzLCBzZF9hbGxfcmVwcywgZ3JvdXAgPSByZXBzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGJldHdlZW5fcGxhdGVfc2QpICsgCiAgbGFicyhzdWJ0aXRsZSA9ICI0IC0gMTYgcmVwbGljYXRlcyBvbiA0IHBsYXRlcyIsIAogICAgICAgdGl0bGUgPSAiRXN0aW1hdGUgU0QgdXNpbmcgd2l0aGluIHBsYXRlIHJlcGVhdHMiLCAKICAgICAgIHkgPSAiZXN0aW1hdGVkIFNEIikKYGBgCmBgYHtyfQpzaW1fb3Zlcl9wbGF0ZXMgPC0gaW1hcF9kZnIoMToxMDAwLCB+IG1hcF9kZnIoMzoyMCwgfiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2ltdWxhdGluZ194bWFwX2RhdGEocGxhdGVzX251bSA9IC54KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmlkID0gInBsYXRlcyIpLCAKICAgICAgICAgLmlkID0gInNpbSIpICU+JSAKICBtdXRhdGUocGxhdGVzID0gYXMuZG91YmxlKHBsYXRlcykpICU+JSAKICBtdXRhdGUocGxhdGVzID0gcGxhdGVzICsgMykKYGBgCgoqKkZpeCByZXBzLCB2YXJ5IHBsYXRlcyoqICAgCgpgYGB7cn0Kc2ltX292ZXJfcGxhdGVzICU+JSAKICBnZ3Bsb3QoLixhZXMocGxhdGVzLCBzZF9hbGxfcGxhdGVzLCBncm91cCA9IHBsYXRlcykpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBiZXR3ZWVuX3BsYXRlX3NkKSArIAogIGxhYnMoc3VidGl0bGUgPSAiOCByZXBlYXRzIG9uIGVhY2ggcGxhdGUiLCAKICAgICAgIHRpdGxlID0gIkVzdGltYXRlIFNEIHVzaW5nIGJldHdlZW4gcGxhdGUgcmVwZWF0cyIsIAogICAgICAgeSA9ICJFc3RpbWF0ZWQgU0QiKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZnJvbSA9IDQsIHRvID0gMjAsIGJ5ID0gMSkpCgpgYGAKCgoqKlZhcnkgYm90aCBwbGF0ZXMgYW5kIHJlcHMqKiAgICAKCgpgYGB7cn0KcGFyYW1zIDwtIGNyb3NzaW5nKHJlcHMgPSBzZXEoZnJvbSA9IDQsIHRvID0gMTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDIpLCAKICAgICAgICAgICAgICAgICAgIHBsYXRlcyA9IHNlcShmcm9tICA9IDMsIHRvID0gMjAsIGJ5ID0gMikpCgpzaW1fcGFyYW0gPC0gaW1hcF9kZnIoMToxMDAwLCB+IHBhcmFtcyAlPiUgCiAgcm93d2lzZSgpICU+JSAKICBzdW1tYXJpc2UocmVzdWx0cyA9IHNpbXVsYXRpbmdfeG1hcF9kYXRhKHBsYXRlc19udW0gPSBwbGF0ZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwc19udW0gPSByZXBzKSkgJT4lIAogIHVubmVzdChjb2xzID0gYyhyZXN1bHRzKSkgJT4lIAogIGJpbmRfY29scyhwYXJhbXMpLCAKICAuaWQgPSAic2ltIikKYGBgCgoKYGBge3J9CnAgPC0gc2ltX3BhcmFtICU+JSAKICBncm91cF9ieShyZXBzLCBwbGF0ZXMpICU+JSAKICBzdW1tYXJpc2Uoc2RfcmVwcyA9IG1lYW4oc2RfYWxsX3JlcHMpLCAKICAgICAgICAgICAgc2RfcGxhdGVzID0gbWVhbihzZF9hbGxfcGxhdGVzKSkgJT4lIAogIGdncGxvdCguLCBhZXMocmVwcywgc2RfcGxhdGVzKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHBsYXRlcykpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnMgPSBjKGZyZWVub21lX2NvbG9yc1sxXSwgZnJlZW5vbWVfY29sb3JzWzJdLCBmcmVlbm9tZV9jb2xvcnNbM10pKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGJldHdlZW5fcGxhdGVfc2QpICsgCiAgbGFicyh5ID0gIkVzdGltYXRlZCBTRCIpCgpnZ3Bsb3RseShwKQpgYGAKCgoKCgoKCg==